/*
 * Copyright (C) 2017  即时通讯网(52im.net) & Jack Jiang.
 * The MobileIMSDK_X_netty (MobileIMSDK v3.x Netty版) Project. 
 * All rights reserved.
 * 
 * > Github地址: https://github.com/JackJiang2011/MobileIMSDK
 * > 文档地址: http://www.52im.net/forum-89-1.html
 * > 即时通讯技术社区：http://www.52im.net/
 * > 即时通讯技术交流群：320837163 (http://www.52im.net/topic-qqgroup.html)
 *  
 * "即时通讯网(52im.net) - 即时通讯开发者社区!" 推荐开源工程。
 * 
 * LogicProcessor.java at 2017-12-9 11:24:32, code by Jack Jiang.
 * You can contact author with jack.jiang@52im.net or jb2011@163.com.
 */
package net.openmob.mobileimsdk.server.processor;

import io.netty.channel.Channel;
import net.nettime.mobileimsdk.server.bridge.QoS4SendDaemonB2C;
import net.nettime.mobileimsdk.server.netty.MBObserver;
import net.openmob.mobileimsdk.server.ServerCoreHandler;
import net.openmob.mobileimsdk.server.protocal.Protocal;
import net.openmob.mobileimsdk.server.protocal.ProtocalFactory;
import net.openmob.mobileimsdk.server.protocal.c.PLoginInfo;
import net.openmob.mobileimsdk.server.qos.QoS4ReciveDaemonC2S;
import net.openmob.mobileimsdk.server.qos.QoS4SendDaemonS2C;
import net.openmob.mobileimsdk.server.utils.GlobalSendHelper;
import net.openmob.mobileimsdk.server.utils.LocalSendHelper;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class LogicProcessor
{
	private static Logger logger = LoggerFactory.getLogger(LogicProcessor.class);  
	
	private ServerCoreHandler serverCoreHandler = null;

	public LogicProcessor(ServerCoreHandler serverCoreHandler)
	{
		this.serverCoreHandler = serverCoreHandler;
	}

	//处理C2C消息
	public void processC2CMessage(BridgeProcessor bridgeProcessor,
			Channel session, Protocal pFromClient, String remoteAddress) throws Exception
	{
		//发送C2C数据
		GlobalSendHelper.sendDataC2C(bridgeProcessor, session, pFromClient
				, remoteAddress, this.serverCoreHandler);
	}
	
	public void processC2SMessage(Channel session, final Protocal pFromClient
			, String remoteAddress) throws Exception
	{
		//判断是否开启QOS，开启QOS可以保证消息的可靠性，
		if(pFromClient.isQoS())// && processedOK)
		{
			//判断此消息是否已经收到过了
			if(QoS4ReciveDaemonC2S.getInstance().hasRecieved(pFromClient.getFp()))
			{
				//是否开启DEBUG
				if(QoS4ReciveDaemonC2S.getInstance().isDebugable())
					logger.debug("[IMCORE-本机QoS！]【QoS机制】"+pFromClient.getFp()
							+"已经存在于发送列表中，这是重复包，通知业务处理层收到该包罗！");
				//将当前接受的对象存QoS4ReciveDaemonC2S的recievedMessages属性里面
				QoS4ReciveDaemonC2S.getInstance().addRecieved(pFromClient);
				//此方法用来答复客户端，服务端已经收到消息了
				LocalSendHelper.replyDelegateRecievedBack(session
						, pFromClient
						, new MBObserver(){
							@Override
							public void update(boolean receivedBackSendSucess, Object extraObj)
							{
								if(receivedBackSendSucess)
									logger.debug("[IMCORE-本机QoS！]【QoS_应答_C2S】向"+pFromClient.getFrom()+"发送"+pFromClient.getFp()
											+"的应答包成功了,from="+pFromClient.getTo()+".");
							}
						}
				);
				//return代表会调用this.serverCoreHandler.getServerEventListener().onTransBuffer_C2S_CallBack(pFromClient, session);这句代码
				return;
			}
			//将当前接受的对象存QoS4ReciveDaemonC2S的recievedMessages属性里面
			QoS4ReciveDaemonC2S.getInstance().addRecieved(pFromClient);
			//此方法用来答复客户端，服务端已经收到消息了
			LocalSendHelper.replyDelegateRecievedBack(session
					, pFromClient
					, new MBObserver(){
						@Override
						public void update(boolean receivedBackSendSucess, Object extraObj)
						{
							if(receivedBackSendSucess)
								logger.debug("[IMCORE-本机QoS！]【QoS_应答_C2S】向"+pFromClient.getFrom()+"发送"+pFromClient.getFp()
										+"的应答包成功了,from="+pFromClient.getTo()+".");
						}
					}
			);
		}

//		boolean processedOK = this.serverCoreHandler.getServerEventListener().onTransBuffer_CallBack(
//				pFromClient.getTo(), pFromClient.getFrom(), pFromClient.getDataContent()
//				, pFromClient.getFp(), pFromClient.getTypeu(), session);

		//调用回调函数，说明C2S的整个任务完成了
		boolean processedOK = this.serverCoreHandler.getServerEventListener().onTransBuffer_C2S_CallBack(pFromClient, session);
	}
	
	public void processACK(final Protocal pFromClient, final String remoteAddress) throws Exception
	{
		//如果是客户端发给服务端的ACK消息
		if("0".equals(pFromClient.getTo()))
		{
			//获取客户端发给服务端的内容（ACK状态下消息内容就是指纹）
			String theFingerPrint = pFromClient.getDataContent();
			logger.debug("[IMCORE-本机QoS！]【QoS机制_S2C】收到接收者"+pFromClient.getFrom()+"回过来的指纹为"+theFingerPrint+"的应答包.");
			/**如果serverCoreHandler.getServerMessageQoSEventListener()不等于null，
			 * 那么直接调用ServerMessageQoSEventListener接口的的实现类
			 * 的messagesBeReceived方法，并传入参数
			 * */
			if(this.serverCoreHandler.getServerMessageQoSEventListener() != null)
				this.serverCoreHandler.getServerMessageQoSEventListener()
					.messagesBeReceived(theFingerPrint);
			//根据指纹移除一些数据
			QoS4SendDaemonS2C.getInstance().remove(theFingerPrint);
		}
		//如果是客户端发给客户端
		else
		{
			// TODO just for DEBUG
			//打印当前在线人数
			OnlineProcessor.getInstance().__printOnline();
			//获取客户端发给客户端的内容（ACK状态下消息内容就是指纹）
			final String theFingerPrint = pFromClient.getDataContent();
			//判断是否桥接
			boolean isBridge = pFromClient.isBridge();
			
			if(isBridge)
			{
				logger.debug("[IMCORE-桥接QoS！]【QoS机制_S2C】收到接收者"+pFromClient.getFrom()+"回过来的指纹为"+theFingerPrint+"的应答包.");
				QoS4SendDaemonB2C.getInstance().remove(theFingerPrint);
			}
			else
			{
				//构建观察者，以便后面的代码调用
				MBObserver sendResultObserver = new MBObserver(){
					@Override
					public void update(boolean _sendOK, Object extraObj)
					{
						logger.debug("[IMCORE-本机QoS！]【QoS机制_C2C】"+pFromClient.getFrom()+"发给"+pFromClient.getTo()
								+"的指纹为"+theFingerPrint+"的应答包已成功转发？"+_sendOK);
					}
				};
				//发送数据
				LocalSendHelper.sendData(pFromClient, sendResultObserver);
			}
		}
	}

	//用户登陆方法
	public void processLogin(final Channel session, final Protocal pFromClient, final String remoteAddress) throws Exception
	{
		//将json格式的字符串反序列化成对象
		final PLoginInfo loginInfo = ProtocalFactory.parsePLoginInfo(pFromClient.getDataContent());
		logger.info("[IMCORE]>> 客户端"+remoteAddress+"发过来的登陆信息内容是：loginInfo="
				+loginInfo.getLoginUserId()+"|getToken="+loginInfo.getLoginToken());
		//判断对象信息是否为null
		if(loginInfo == null || loginInfo.getLoginUserId() == null)
		{
			logger.warn("[IMCORE]>> 收到客户端"+remoteAddress
					+"登陆信息，但loginInfo或loginInfo.getLoginUserId()是null，登陆无法继续[loginInfo="+loginInfo
					+",loginInfo.getLoginUserId()="+loginInfo.getLoginUserId()+"]！");
			return;
		}
		//判断ServerEventListener的实现是否为null，虽然写的是接口，但实际拿出来的是接口的实现类
		if(serverCoreHandler.getServerEventListener() != null)
		{
			//判断当前相要登陆的账号是否已经登陆了
			boolean alreadyLogined = OnlineProcessor.isLogined(session);//(_try_user_id != -1);
			//如果已经登陆过了
			if(alreadyLogined)
			{
				logger.debug("[IMCORE]>> 【注意】客户端"+remoteAddress+"的会话正常且已经登陆过，而此时又重新登陆：getLoginName="
        				+loginInfo.getLoginUserId()+"|getLoginPsw="+loginInfo.getLoginToken());
				
				MBObserver retObserver = new MBObserver(){
					@Override
					public void update(boolean _sendOK, Object extraObj)
					{
						if(_sendOK)
						{
							//重新在这个session里面塞入登陆者的信息
							session.attr(OnlineProcessor.USER_ID_IN_SESSION_ATTRIBUTE_ATTR).set(loginInfo.getLoginUserId());
							//此方法是将客户端连接服务端后的session存放到 ConcurrentHashMap中，其实netty有类似的channelgroup
							OnlineProcessor.getInstance().putUser(loginInfo.getLoginUserId(), session);
							//调用ServerEventListener的实现类的onUserLoginAction_CallBack方法
							serverCoreHandler.getServerEventListener().onUserLoginAction_CallBack(
									loginInfo.getLoginUserId(), loginInfo.getExtra(), session);
						}
						else
						{
							logger.warn("[IMCORE]>> 发给客户端"+remoteAddress+"的登陆成功信息发送失败了！");
						}
					}
				};
				
				LocalSendHelper.sendData(session
						, ProtocalFactory.createPLoginInfoResponse(0, loginInfo.getLoginUserId()), retObserver);
			}
			//如果没有登陆过
			else
			{
				//调用ServerEventListener的实现类的onVerifyUserCallBack方法
				int code = serverCoreHandler.getServerEventListener().onVerifyUserCallBack(
						loginInfo.getLoginUserId(), loginInfo.getLoginToken(), loginInfo.getExtra(), session);
				//如果code==0 说明用户名密码等信息校验成功
				if(code == 0)
				{
					MBObserver sendResultObserver = new MBObserver(){
						@Override
						public void update(boolean __sendOK, Object extraObj)
						{
							if(__sendOK)
							{
								//将登陆的id放当登陆的session的map里面key是 __user_id__ ，value是登陆的id
								session.attr(OnlineProcessor.USER_ID_IN_SESSION_ATTRIBUTE_ATTR).set(loginInfo.getLoginUserId());
								//此方法是将客户端连接服务端后的session存放到 ConcurrentHashMap中，其实netty有类似的channelgroup
								OnlineProcessor.getInstance().putUser(loginInfo.getLoginUserId(), session);
								//调用ServerEventListener的 实现类 的onUserLoginAction_CallBack方法
								serverCoreHandler.getServerEventListener()
									.onUserLoginAction_CallBack(loginInfo.getLoginUserId(), loginInfo.getExtra(), session);
							}
							else
								logger.warn("[IMCORE]>> 发给客户端"+remoteAddress+"的登陆成功信息发送失败了！");
							
						}
					};
					//回复客户端登陆成功了
					LocalSendHelper.sendData(session
							, ProtocalFactory.createPLoginInfoResponse(code, loginInfo.getLoginUserId())
							, sendResultObserver);
				}
				//如果登陆没有校验成功
				else
				{
					/**
					 * 发送数据告诉客户端登陆失败，userid参数为-1是因为有登陆失败。登陆失败有两种可能
					 * 1.账号不存在，那么直接返回-1
					 * 2.密码错误，但是也不能暴露真正存在的账户的用户名（id）
					 * 所以user_id的参数为-1，实际上这个参数写成其他的也可以，因为客户端判断登陆成功与否也是 是以code是否为0判断的
					 * */

					LocalSendHelper.sendData(session, ProtocalFactory.createPLoginInfoResponse(code, "-1"), null);
				}
			}
		}
		else
		{
			logger.warn("[IMCORE]>> 收到客户端"+remoteAddress+"登陆信息，但回调对象是null，没有进行回调.");
		}
	}
     /**
	  *
	  * 该方法用来处理服务端与客户端的心跳机制
	  * */
	public void processKeepAlive(Channel session, Protocal pFromClient
			, String remoteAddress) throws Exception
	{
		//根据session获取用户id
		String userId = OnlineProcessor.getUserIdFromSession(session);
		//判断用户id是否为null
		if(userId != null)
		{
			//给客户端回复消息
			LocalSendHelper.sendData(ProtocalFactory.createPKeepAliveResponse(userId), null);
		}
		else
		{
			logger.warn("[IMCORE]>> Server在回客户端"+remoteAddress+"的响应包时，调用getUserIdFromSession返回null，用户在这一瞬间掉线了？！");
		}
	}
}
